<template>
	<div class="list" ref="listRef1" @mousedown="drag" @mousemove='dragover' @mouseup='dragend' @mouseout="out">
		<div class="list-item" v-for="(item, index) in listData" :key="index" :orgIndex="index" @click="click">
			<slot :row="item"></slot>
		</div>
	</div>
</template>

<script>
	export default {
		name: 'DragList1',
		props: {
			listData: Array, // list绑定的数据
			isDrag: Boolean, // 是否开启拖拽
			callDrop: Function,
			callClick: Function
		},
		data() {
			return {
				oldListInfo: [], // 拖拽前数据
				listInfo: [], // 拖拽后数据
				draging: null, // 被拖拽元素
				target: null, // 目标元素
				allow: false, // 是否可拖拽，鼠标按下后才可拖拽
				isMove: false // 是否移动，区分click
			}
		},
		created() {
			this.listInfo = this.listData;
		},
		methods: {
			click(e) {
				// 如果是移动状态
				if (this.isMove) return;
				let listItem = this.getParentNode(e.target, "list-item");
				if (!listItem) return;
				let clickItemIndex = this.getIndex(listItem);
				// call 自定义click，返回被点击数据
				if (this.callClick && typeof(this.callClick) == 'function') {
					this.callClick({
						"index": clickItemIndex,
						"item": this.listInfo[clickItemIndex]
					});
				}
			},
			drag(e) {
				this.isMove = false; // 鼠标按下未移动
				// 如果未开启拖拽
				if (!this.isDrag) return;
				// 获取当前点击的item
				let listItem = this.getParentNode(e.target, "list-item");
				if (!listItem) return;
				this.draging = listItem;

				this.allow = true; // 允许拖拽
				this.oldListInfo = this.listInfo; // 当前数据变为旧数据
			},
			dragover(e) {
				// 未开启或者不允许时，不触发
				if (!this.isDrag || !this.allow) return;
				this.isMove = true; // 鼠标按下移动
				// 获取移动到的item
				let listItem = this.getParentNode(e.target, "list-item");
				if (!listItem) return;

				this.target = listItem;
				// 获取两个位置的纵坐标
				let targetTop = this.target.getBoundingClientRect().top;
				let dragingTop = this.draging.getBoundingClientRect().top;

				let dragingIndex = this.getIndex(this.draging);
				let targetIndex = this.getIndex(this.target);

				if (this.target.className === "list-item" && this.target !== this.draging) {
					if (this.target) {
						// 如果正在进行动画
						if (this.target.animated) {
							return;
						}
					}
					if (dragingIndex < targetIndex) {
						this.target.parentNode.insertBefore(this.draging, this.target.nextSibling);
					} else {
						this.target.parentNode.insertBefore(this.draging, this.target);
					}
					this.animate(dragingTop, this.draging);
					this.animate(targetTop, this.target);
				}
			},
			dragend() {
				this.allow = false; // 重置状态

				if (!this.isDrag || !this.isMove) return;
				// 获取拖拽后数据
				this.listInfo = this.getNewData();
				// call自定义事件，返回新旧数据
				if (this.callDrop && typeof(this.callDrop) == 'function') {
					this.callDrop({
						"oldListInfo": this.oldListInfo,
						"newListInfo": this.listInfo
					});
				}
				this.isMove = false; // 重置状态
			},
			out(e) {
				if (!this.allow) return;
				e = e || window.event;
				var obj = e.toElement || e.relatedTarget;
				// 鼠标移出list范围
				if (!this.$refs.listRef1.contains(obj)) {
					this.allow = false;
					this.listInfo = this.getNewData();
					if (this.callDrop && typeof(this.callDrop) == 'function') {
						this.callDrop({
							"oldListInfo": this.oldListInfo,
							"newListInfo": this.listInfo
						});
					}
					this.isMove = false;
				}
			},
			/*
			 * 按类名查询父元素
			 * node：当前节点
			 * pclassname：父元素类名
			 */
			getParentNode(node, pclassname) {
				let p = node;
				while (p.parentNode) {
					if (p.parentNode.className == pclassname) {
						return p.parentNode;
					} else {
						p = p.parentNode;
					}
					if (p.nodeName == "BODY") return 0;
				}
			},
			/*
			 * 获取元素在父元素的索引
			 * node：当前节点
			 * pclassname：父元素类名
			 */
			getIndex(el) {
				let domData = Array.from(this.$refs.listRef1.childNodes);
				return domData.findIndex(i => i.innerText == el.innerText);
			},
			// 动画效果
			animate(startPos, dom) {
				let offset = startPos - dom.getBoundingClientRect().top;
				dom.style.transition = "none";
				dom.style.transform = `translateY(${offset}px)`;

				dom.offsetWidth;
				dom.style.transition = "transform .3s";
				dom.style.transform = "";
				clearTimeout(dom.animated);

				dom.animated = setTimeout(() => {
					dom.style.transition = "";
					dom.style.transform = "";
					dom.animated = false;
				}, 300)
			},
			// 获取拖拽后的数据
			getNewData() {
				let currentNodes = Array.from(this.$refs.listRef1.childNodes);
				let listArr = [];
				currentNodes.map((i, index) => {
					return listArr[index] = this.listData[i.getAttribute("orgIndex")];
				});
				return listArr;
			}
		}
	}
</script>

<style scoped>
	.list {
		display: block;
	}

	.list-item {
		width: 100%;
		height: 100%;
		user-select: none;
		cursor: pointer;
	}
</style>
